﻿#pragma once
#include "tools.hpp"
#include <filesystem>

#if ( defined( _WIN32 ) || defined( _WIN64 ) )
#define GLOG_NO_ABBREVIATED_SEVERITIES //我自己的Windows平台上有这个需求
#endif

#include <glog/logging.h>
#include <glog/raw_logging.h>

#if ( defined( __linux__ ) )
#include <pwd.h>
#endif

#include <cstdio>
#include <string>

template < typename... Args >
inline void MYLOG_INFO( const char *fmt, Args... args )
{
    std::string str = MyUtils::Tools::string_format( fmt, args... );
    LOG( INFO ) << str;
}

template < typename... Args >
inline void MYLOG_ERROR( const char *fmt, Args... args )
{
    std::string str = MyUtils::Tools::string_format( fmt, args... );
    LOG( ERROR ) << str;
}

template < typename... Args >
inline void MYLOG_DEBUG_INFO( const char *fmt, Args... args )
{
#ifdef MYLOG_DEBUG
    std::string str = MyUtils::Tools::string_format( fmt, args... );
    DLOG( INFO ) << str;
#endif
}

template < typename... Args >
inline void MYLOG_DEBUG_ERROR( const char *fmt, Args... args )
{
#ifdef MYLOG_DEBUG
    std::string str = MyUtils::Tools::string_format( fmt, args... );
    DLOG( ERROR ) << str;
#endif
}

#if ( defined( __linux__ ) )

#define MYLOG_INFO( s, a... )                                                  \
    {                                                                          \
        char temp[ 1024 ];                                                     \
        sprintf( temp, ( s ), ##a );                                           \
        LOG( INFO ) << temp;                                                   \
    }

#define MYLOG_ERROR( s, a... )                                                 \
    {                                                                          \
        char temp[ 1024 ];                                                     \
        sprintf( temp, ( s ), ##a );                                           \
        LOG( ERROR ) << temp;                                                  \
    }

#ifndef MYLOG_DEBUG
#define MYLOG_DEBUG_INFO( s, a... )                                            \
    {                                                                          \
        char temp[ 1024 ];                                                     \
        sprintf( temp, ( s ), ##a );                                           \
        DLOG( INFO ) << temp;                                                  \
    }
#else
#define MYLOG_DEBUG_INFO( s, a... )                                            \
    {                                                                          \
    }
#endif

#ifndef MYLOG_DEBUG
#define MYLOG_DEBUG_ERROR( s, a... )                                           \
    {                                                                          \
        char temp[ 1024 ];                                                     \
        sprintf( temp, ( s ), ##a );                                           \
        DLOG( ERROR ) << temp;                                                 \
    }
#else
#define MYLOG_DEBUG_ERROR( s, a... )                                           \
    {                                                                          \
    }
#endif

#endif

namespace MyUtils
{
class GlogHelper
{
  private:
    GlogHelper( const GlogHelper & ) = delete;
    GlogHelper &operator=( const GlogHelper & ) = delete;
    GlogHelper( GlogHelper && ) = delete;
    GlogHelper &operator=( GlogHelper && ) = delete;

  public:
    /**
     * @brief 初始化glog。如果parent_path为NULL则表示不输出到文件中
     * @param [in] log_to_file 指示是否输出到文件。
     * @param [in] parent_path
     * 日志文件所处的文件夹的路径。如果该路径不存在则会创建。如果该参数为NULL，则默认
     * 输出到$HOME/.log/$PARAGRAM_NAME文件夹下。Windows平台下，如果该参数为NULL，则默认
     * 路径是C:\.log\$PARAGRAM_NAME。
     */
    explicit GlogHelper( bool log_to_file, const char *parent_path = NULL )
    {
        // 初始化日志库
        std::string program_name = std::get< 0 >( Tools::getProcessNameAndId( ) );
        google::InitGoogleLogging( program_name.c_str( ) );

        FLAGS_logtostderr =
            log_to_file ? 0 : 1; //当需要输出到文件的时候，这个必须设置为0
        FLAGS_alsologtostderr = 1; // 日志同时输出到stderr
        FLAGS_colorlogtostderr = true;

        google::InstallFailureSignalHandler( );  // 捕捉信号
        google::InstallFailureWriter( &writer ); // 打印错误信息

        if ( log_to_file )
        {
            FLAGS_stop_logging_if_full_disk =
                true; //设置是否在磁盘已满时避免日志记录到磁盘
            FLAGS_logbufsecs = 0; //设置可以缓冲日志的最大秒数，0指实时输出

            //构造输出路径
            std::string process_log = program_name + ".log";
            std::string log_dir;

            if ( parent_path )
            {
                log_dir = std::string( parent_path );
            }
            else
            {

#if ( defined( __linux__ ) )
                struct passwd *pw = getpwuid( getuid( ) );
                log_dir = std::string( pw->pw_dir );
                log_dir += "/.log/" + program_name + "/";

#endif

#if ( defined( _WIN32 ) )
                log_dir = "C:/.log/" + program_name + "/";
#endif
            }

            if ( *( log_dir.end( ) - 1 ) != '/' && *( log_dir.end( ) - 1 ) != '\\' )
                log_dir += '/';
            
            

            // 创建日志文件夹
            if ( !std::filesystem::exists( log_dir ) )
            {
                std::filesystem::create_directories( log_dir );
            }

            FLAGS_log_dir = log_dir;

            // system( ( "mkdir -p " + homedir ).c_str( ) );

            google::SetLogDestination(
                google::GLOG_ERROR,
                ( log_dir + process_log + ".ERROR" ).c_str( ) );
            google::SetLogDestination(
                google::GLOG_WARNING,
                ( log_dir + process_log + ".WARN" ).c_str( ) );
            google::SetLogDestination(
                google::GLOG_INFO,
                ( log_dir + process_log + ".INFO" ).c_str( ) );

            LOG( INFO ) << "glog to file: " << log_dir;
        }

        return;
    }
    ~GlogHelper( ) { google::ShutdownGoogleLogging( ); }

  private:
    static void( writer )( const char *data, int size )
    {
        LOG( ERROR ) << data;
    }
};

} // namespace MyUtils